import pandas as pd
import matplotlib.pyplot as plt
from reportlab.lib.pagesizes import letter
from reportlab.pdfgen import canvas
import os

# 定义图表类型及其生成函数，使用字典存储，键为图表类型，值为对应的绘图匿名函数
CHART_TYPES = {
    'bar': lambda df: df.plot(kind='bar'),
    'line': lambda df: df.plot(kind='line'),
    'hist': lambda df: df.plot(kind='hist'),
    'pie': lambda df: df.iloc[:, 0].plot(kind='pie', ylabel='')  # 饼图通常只处理一列数据，去掉多余的 y 轴标签
}

def excel_to_visual_report(excel_file_path, output_pdf_path, selected_charts):
    """
    将 Excel 文件中的数据转换为包含可视化图表的 PDF 报表。

    :param excel_file_path: 输入的 Excel 文件路径
    :param output_pdf_path: 输出的 PDF 报表文件路径
    :param selected_charts: 用户选择的图表类型列表
    """
    try:
        # 读取 Excel 文件，将数据存储到 DataFrame 中
        df = pd.read_excel(excel_file_path)
    except FileNotFoundError:
        print(f"未找到 Excel 文件: {excel_file_path}")
        return

    # 检查 DataFrame 是否为空
    if df.empty:
        print("Excel 文件中没有数据，无法生成图表。")
        return

    chart_paths = []  # 用于存储生成的图表图片的文件路径
    page_y = 750  # PDF 页面的初始 Y 坐标，用于定位元素

    # 创建一个 PDF 画布对象，指定输出路径和页面尺寸
    c = canvas.Canvas(output_pdf_path, pagesize=letter)
    # 设置 PDF 中文本的字体和大小
    c.setFont('Helvetica', 16)
    # 在 PDF 页面上绘制标题文本
    c.drawString(100, page_y, 'Excel 数据可视化报表')
    page_y -= 30  # 调整 Y 坐标，为后续元素腾出空间

    # 遍历用户选择的图表类型
    for chart_type in selected_charts:
        if chart_type in CHART_TYPES:  # 检查选择的图表类型是否有效
            plt.figure(figsize=(10, 6))
            try:
                if chart_type == 'pie' and df.shape[1] > 1:
                    print("饼图仅支持一列数据，将使用第一列数据绘制。")
                # 调用对应的图表生成函数绘制图表
                CHART_TYPES[chart_type](df)
                # 设置图表的标题
                plt.title(f'示例 {chart_type} 图')
                # 设置图表 X 轴的标签
                plt.xlabel('品类')
                # 设置图表 Y 轴的标签，饼图不设置
                if chart_type != 'pie':
                    plt.ylabel('金额')
                # 自动调整图表布局，避免元素重叠
                plt.tight_layout()
                # 定义图表图片的文件名
                chart_image_path = f'temp_{chart_type}.png'
                # 将绘制好的图表保存为图片文件
                plt.savefig(chart_image_path)
                # 关闭当前绘图窗口，释放资源
                plt.close()
                # 将图表图片的文件路径添加到列表中
                chart_paths.append(chart_image_path)
            except Exception as e:
                print(f"生成 {chart_type} 图表时出错: {e}")

    # 遍历存储图表图片路径的列表
    for chart_path in chart_paths:
        if page_y < 100:  # 检查当前 Y 坐标是否低于页面底部阈值
            c.showPage()  # 新建一个 PDF 页面
            page_y = 750  # 重置 Y 坐标到页面顶部
        try:
            # 在 PDF 页面上绘制图表图片
            c.drawImage(chart_path, 100, page_y - 300, width=400, height=300)
            page_y -= 320  # 调整 Y 坐标，为下一个元素腾出空间
        except Exception as e:
            print(f"将 {chart_path} 插入 PDF 时出错: {e}")
        finally:
            if os.path.exists(chart_path):
                os.remove(chart_path)  # 删除临时生成的图表图片文件

    c.save()  # 保存并关闭 PDF 文件

if __name__ == "__main__":
    # 替换为你的 Excel 文件路径，可修改为实际文件路径
    excel_file = 'D:\\pythonworkspace\\pythonplay\\play_python\\Excel\\新建 XLSX 工作表.xlsx'
    # 替换为你想要的输出 PDF 路径，可修改为实际文件路径
    output_pdf = 'visual_report.pdf'

    print("请选择要生成的图表类型（输入编号，用逗号分隔，如 1,2）：")
    print("1. 柱状图 (bar)")
    print("2. 折线图 (line)")
    print("3. 直方图 (hist)")
    print("4. 饼图 (pie)")
    # 获取用户输入的图表类型编号
    selection = input("输入选择: ")

    # 定义编号到图表类型的映射字典
    number_to_chart = {
        '1': 'bar',
        '2': 'line',
        '3': 'hist',
        '4': 'pie'
    }

    # 根据用户输入的编号，筛选出有效的图表类型并存储到列表中
    selected_charts = [number_to_chart[num] for num in selection.split(',') if num in number_to_chart]

    if not selected_charts:  # 检查用户是否选择了有效的图表类型
        print("未选择有效的图表类型，程序退出。")
    else:
        # 调用函数生成可视化报表
        excel_to_visual_report(excel_file, output_pdf, selected_charts)